package nz.ac.massey.jfontview;

import java.awt.Font;
import java.util.Collection;
import java.util.List;
import java.util.Vector;

import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

/**
 * A list of fonts which can be used in both a JTable or JList.
 * @author Nathan C Jones
 * @version 1.0
 */
public class FontList implements ListModel, TableModel {
	
	/**
	 * All fonts currently in the font list.
	 */
	private List<Font> fonts;
	
	/**
	 * All currently registered list data listeners.
	 */
	private Collection<ListDataListener> listDataListeners = new Vector<ListDataListener>();
	
	/**
	 * All currently registered table model listeners.
	 */
	private Collection<TableModelListener> tableModelListeners = new Vector<TableModelListener>();

	/**
	 * Create a new empty font list.
	 */
	public FontList(){
		this(new Vector<Font>());
	}
	
	/**
	 * Create a new font list initialised with the list of fonts.
	 * @param fonts the list of fonts to initialise the font list with.
	 */
	public FontList(List<Font> fonts){
		this.fonts = fonts;
	}
	
	/**
	 * Add a font to the end of list.
	 * @param font the font to be added.
	 */
	public void add(Font font){
		fonts.add(font);
		fontAdded(fonts.size()-1);
	}

	/**
	 * Add a font to the specified index of the list.
	 * @param index the position in the list to insert the font into.
	 * @param font the font to be inserted.
	 */
	public void add(int index, Font font){
		fonts.add(index,font);
		fontAdded(index);
	}
	
	/**
	 * Remove a font from the given index of the list.
	 * @param index the index of the font to remove.
	 */
	public void remove(int index){
		fonts.remove(index);
		fontRemoved(index);
	}

	/**
	 * Retrieve the font from the given index.
	 * The state of the list is unchanged.
	 * @param index the index of the font to retrieve from the list.
	 * @return the font at the given index.
	 */
	public Font get(int index){
		return fonts.get(index);
	}

	/*
	 * List model methods: 
	 */
	
	public int getSize() {
		return fonts.size();
	}
	
	public Object getElementAt(int index) {
		Font f = get(index); 
		String fontString = f.getName();
		fontString = fontString + ", " + f.getSize();
		fontString = fontString + ", " + f.getStyle();
		return fontString;
	}
	
	public void addListDataListener(ListDataListener l) {
		listDataListeners.add(l);
	}

	public void removeListDataListener(ListDataListener l) {
		listDataListeners.remove(l);
	}
	
	/*
	 * Table model methods:
	 */

	public int getRowCount(){
		return fonts.size();
	}

	public int getColumnCount(){
		return 3;
	}

	public String getColumnName(int columnIndex){
		switch(columnIndex){
		case 0:
			return "Name";
		case 1:
			return "Style";
		case 2:
			return "Size";
		default:
			throw new RuntimeException("Invalid column index");
		}
	}

	public Class<?> getColumnClass(int columnIndex){
		switch(columnIndex){
		case 0:
			return String.class;
		case 1:
			return String.class;
		case 2:
			return String.class;
		default:
			throw new RuntimeException("Invalid column index");
		}
	}

	public boolean isCellEditable(int rowIndex, int columnIndex){
		// TODO Auto-generated method stub
		return false;
	}

	public Object getValueAt(int rowIndex, int columnIndex){
		Font f = fonts.get(rowIndex);
		switch(columnIndex){
		case 0:
			return f.getName();
		case 1:
			switch(f.getStyle()){
			case 0:
				return "Plain";
			case 1:
				return "Bold";
			case 2:
				return "Italic";
			default:
				return "Bold Italic";
			}
		case 2:
			return f.getSize();
		default:
			throw new RuntimeException("Invalid column index");
		}
	}

	public void setValueAt(Object aValue, int rowIndex, int columnIndex){
		Font oldFont = get(rowIndex);
		Font newFont;
		switch(columnIndex){
		case 0:
			String name = (String)aValue;
			newFont = new Font(name,oldFont.getStyle(),oldFont.getSize());
			break;
		case 1:
			Integer style = (Integer)aValue;
			newFont = oldFont.deriveFont(style);
			break;
		case 2:
			Integer size = (Integer)aValue;
			newFont = oldFont.deriveFont(size);
		default:
			throw new RuntimeException("Invalid column index");
		}
		remove(rowIndex);
		add(rowIndex,newFont);
	}

	public void addTableModelListener(TableModelListener listener){
		tableModelListeners.add(listener);
	}

	public void removeTableModelListener(TableModelListener listener){
		tableModelListeners.remove(listener);
	}
	
	/*
	 * Observer notification methods:
	 */

	private void fontAdded(int index){
		for(ListDataListener l:listDataListeners){
			l.intervalAdded(new ListDataEvent(this,ListDataEvent.INTERVAL_ADDED,index,index));
		}
		for(TableModelListener l:tableModelListeners){
			l.tableChanged(new TableModelEvent(this,index,index,TableModelEvent.ALL_COLUMNS,TableModelEvent.INSERT));
		}
	}
	
	private void fontRemoved(int index){
		for(ListDataListener l:listDataListeners){
			l.intervalAdded(new ListDataEvent(this,ListDataEvent.INTERVAL_REMOVED,index,index));
		}
		for(TableModelListener l:tableModelListeners){
			l.tableChanged(new TableModelEvent(this,index,index,TableModelEvent.ALL_COLUMNS,TableModelEvent.DELETE));
		}
	}
}
